using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using Moonlite.Services.CodeCompletion;
using Moonlite.ExtensionApi;
using Moonlite.ExtensionApi.ControlInterfaces;
using Moonlite.ExtensionApi.GlobalInterfaces;
using Moonlite.Platform.General;
using Moonlite.Platform.Project;
using Moonlite.Platform.Text.File;

namespace Moonlite.UI.Shell.Controls
{
    public partial class ProjectManager : UserControl, IProjectManager
    {
        private const string TreeViewStateFile = "ProjectManagerState.xml";

        public event EnabledItemsChangedEventHandler EnabledItemsChanged;
        public event ItemOpenEventHandler ItemOpen;
        private event EventHandler Paste;
        private Timer updateFileNamesTimer;
        private Hashtable folders = new Hashtable();
        public Hashtable Folders
        {
            get { return folders; }
            set { folders = value; }
        }

        private bool nativeFileAssociations;
        private NativeNode nativeNode;

        private TreeNode PastedNode { get; set; }
        private TreeNode SourceNode { get; set; }

        private IProjectFolder FolderTag { get; set; }
        private IProjectFile FileTag { get; set; }
        private string TagType { get; set; }

        private static ToolStripItemCollection CustomItems { get; set; }
        internal static Hashtable NativeAssociatedActions { get; set; }
        internal static Hashtable AssociatedActions { get; set; }
        private static IProjectFile FoundSourceFile { get; set; }
        private static IProjectFolder FoundSourceFolder { get; set; }

        private static bool StateFileHasBeenSaved { get; set; }
        private static bool StateFileHasBeenRead { get; set; }
        private static SerializableDictionary ReadState { get; set; }
        protected virtual void OnEnabledItemsChanged(EnabledItemsChangedEventArgs e)
        {
            if (EnabledItemsChanged != null)
            {
                EnabledItemsChanged(this, e);
            }
        }

        protected virtual void OnPaste(EventArgs e)
        {
            if (Paste != null)
            {
                Paste(this, e);
            }
        }

        public void AddFileExtensionAssociation(string extension, ProjectManagerOpenFileAction action)
        {
            if (!nativeFileAssociations && NativeAssociatedActions.ContainsKey(extension))
            {
                NativeAssociatedActions.Remove(extension);
                AssociatedActions.Remove(extension);
            }

            if (AssociatedActions.ContainsKey(extension))
            {
                ((List<ProjectManagerOpenFileAction>)AssociatedActions[extension]).Add(action);
                if (nativeFileAssociations)
                {
                    ((List<ProjectManagerOpenFileAction>)NativeAssociatedActions[extension]).Add(action);
                }
                
            }
            else
            {
                AssociatedActions.Add(extension, new List<ProjectManagerOpenFileAction>(new ProjectManagerOpenFileAction[] { action }));
                if (nativeFileAssociations)
                {
                    NativeAssociatedActions.Add(extension, new List<ProjectManagerOpenFileAction>(new ProjectManagerOpenFileAction[] { action }));
                }
            }
        }

        public void AddCustomItem(ToolStripMenuItem item)
        {
            CustomItems.Add(item);
            UpdateContextMenu(treeView.SelectedNode);
        }

        public void RemoveCustomItem(ToolStripItem item)
        {
            if (CustomItems.Contains(item))
            {
                CustomItems.Remove(item);
                if (contextMenuStrip.Items.Contains(item))
                {
                    contextMenuStrip.Items.Remove(item);
                }
            }
        }

        public void AddCustomNode(AbstractCustomNode node)
        {
            if (Global.Instance.Project != null)
            {
                treeView.Nodes[0].Nodes.Add(node);
            }
        }

        public void RemoveCustomNode(AbstractCustomNode node)
        {
            if (treeView.Nodes.Count > 0 && treeView.Nodes[0] != null && treeView.Nodes[0].Nodes.Contains(node))
            {
                treeView.Nodes[0].Nodes.Remove(node);
            }
        }

        internal void CloseProject()
        {
            treeView.Nodes.Clear();
        }

        internal void OpenProject(IProject project)
        {
            Global.Instance.Text = project.Name + " - Moonlite Map Studio";

            treeView.Nodes.Clear();
            int projectImageIndex = project.Type == ProjectType.Galaxy ? Global.GalaxyProjectImageIndex : project.Type == ProjectType.Jass ? Global.JassProjectImageIndex : Global.ProjectImageIndex;
            TreeNode projectNode = new TreeNode(project.Name) { ImageIndex = projectImageIndex, SelectedImageIndex = projectImageIndex };
            if (project.Type == ProjectType.Galaxy)
            {
                nativeNode = new NativeNode(project);
                projectNode.Nodes.Add(nativeNode);
            }

            foreach (IProjectFile file in project.Files)
            {
                projectNode.Nodes.Add(new TreeNode(file.Name)
                {
                    Tag = file,
                    ImageIndex = file.GetImageIndex(),
                    SelectedImageIndex = file.GetImageIndex()
                });
            }

            foreach (IProjectFolder folder in project.Folders)
            {
                projectNode.Nodes.Add(GetFolderNode(folder));
            }

            projectNode.Tag = project;
            projectNode.Expand();
            treeView.Nodes.Add(projectNode);
            treeView.Sort();
            Global.Instance.UpdateEnabledItems(null, null);
            ((IFunctionExplorer)((object[])(Global.Instance.GetService(typeof(IFunctionExplorer))))[0]).UpdateNativeCode();
            if (Global.Instance.Project.Type == ProjectType.Jass) JassInheritanceManager.Update();
        }

        public static TreeNode GetFolderNode(IProjectFolder folder)
        {
            TreeNode folderNode = new TreeNode(folder.Name) { Tag = folder };

            foreach (IProjectFile file in folder.Files)
            {
                folderNode.Nodes.Add(new TreeNode(file.Name)
                {
                    Tag = file,
                    ImageIndex = file.GetImageIndex(),
                    SelectedImageIndex = file.GetImageIndex()
                });
            }

            foreach (IProjectFolder childFolder in folder.Folders)
            {
                childFolder.Parent = folder;
                folderNode.Nodes.Add(GetFolderNode(childFolder));
            }

            return folderNode;
        }

        private void OnTreeViewAfterSelect(object sender, TreeViewEventArgs e)
        {
            UpdateContextMenu(e.Node);
            UpdateTreeNodes(treeView.Nodes[0]);
            UpdatePackageName();
        }

        private void UpdatePackageName()
        {
            if (treeView.SelectedNode == null) return;
            if (Global.Instance == null || Global.Instance.Project == null) return;
            if (IsTreeNodeFile(treeView.SelectedNode))
            {
                if (GetTreeNodeFile(treeView.SelectedNode).Parent == null)
                {
                    AvailableProperties.Package = Global.Instance.Project.Name.Replace(" ", "_");
                }
                else
                {
                    AvailableProperties.Package = Global.Instance.Project.Name.Replace(" ", "_") + "." + GetTreeNodeFile(treeView.SelectedNode).Path.Replace(Global.Instance.Project.Path, "").Replace("\\", ".");
                }
            }
            else if (IsTreeNodeFolder(treeView.SelectedNode))
            {
                AvailableProperties.Package = Global.Instance.Project.Name.Replace(" ", "_") + "." + GetFolderPath(GetTreeNodeFolder(treeView.SelectedNode)).Replace(Global.Instance.Project.Path, "").Replace("\\", ".");
            }
            else
            {
                AvailableProperties.Package = Global.Instance.Project.Name.Replace(" ", "_");
            }
        }

        private void UpdateContextMenu(TreeNode node)
        {
            itmRename.Enabled = true;
            if (node is AbstractCustomNode && node.ContextMenuStrip != null)
            {
                ContextMenuStrip = node.ContextMenuStrip;
            }
            else
            {
                ContextMenuStrip = contextMenuStrip;
            }

            if (node is NativeNode || node.Tag is string || (node is AbstractCustomNode && node.ContextMenuStrip == null))
            {
                itmRename.Enabled = false;
                itmAdd.Enabled = false;
                itmOpen.Enabled = false;
                itmViewProjectInformation.Enabled = false;
                itmDelete.Enabled = false;
                itmCopy.Enabled = false;
                itmCut.Enabled = false;
                itmOpenFolderInWindowsExplorer.Enabled = false;
                itmPaste.Enabled = false;
                return;
            }

            if (node.Parent == null)
            {
                itmAdd.Enabled = true;
                itmOpen.Enabled = false;
                itmViewProjectInformation.Enabled = true;
                itmDelete.Enabled = false;
                itmCut.Enabled = false;
                itmCopy.Enabled = false;
                itmOpenFolderInWindowsExplorer.Enabled = false;
                CheckPasteEnabled();
                OnEnabledItemsChanged(new EnabledItemsChangedEventArgs(SelectedItemType.Project));
            }
            else if (node.Tag is IProjectFile)
            {
                if (((IProjectFile)node.Tag).ReadOnly)
                {
                    itmRename.Enabled = false;
                    itmAdd.Enabled = false;
                    itmOpen.Enabled = false;
                    itmViewProjectInformation.Enabled = false;
                    itmDelete.Enabled = false;
                    itmCopy.Enabled = false;
                    itmCut.Enabled = false;
                    itmOpenFolderInWindowsExplorer.Enabled = false;
                    itmPaste.Enabled = false;
                }
                else
                {
                    itmAdd.Enabled = false;
                    itmOpen.Enabled = true;
                    itmViewProjectInformation.Enabled = false;
                    itmDelete.Enabled = true;
                    itmCut.Enabled = true;
                    itmCopy.Enabled = true;
                    itmOpenFolderInWindowsExplorer.Enabled = false;
                    CheckPasteEnabled();
                }

                OnEnabledItemsChanged(new EnabledItemsChangedEventArgs(SelectedItemType.File));
            }
            else
            {
                if (((IProjectFolder)node.Tag).ReadOnly)
                {
                    itmRename.Enabled = false;
                    itmAdd.Enabled = false;
                    itmOpen.Enabled = false;
                    itmViewProjectInformation.Enabled = false;
                    itmDelete.Enabled = false;
                    itmCopy.Enabled = false;
                    itmCut.Enabled = false;
                    itmOpenFolderInWindowsExplorer.Enabled = false;
                    itmPaste.Enabled = false;
                }
                else
                {
                    itmAdd.Enabled = true;
                    itmOpen.Enabled = false;
                    itmViewProjectInformation.Enabled = false;
                    itmDelete.Enabled = true;
                    itmCut.Enabled = true;
                    itmCopy.Enabled = true;
                    itmOpenFolderInWindowsExplorer.Enabled = true;
                    CheckPasteEnabled();
                }

                OnEnabledItemsChanged(new EnabledItemsChangedEventArgs(SelectedItemType.Folder));
            }
        }

        private bool CheckPasteEnabled()
        {
            if (Clipboard.GetDataObject() != null)
            {
                itmPaste.Enabled = true;
                return true;
            }

            itmPaste.Enabled = false;
            return false;
        }

        public TreeNode AddFolder(string name, bool rename)
        {
            if (treeView.SelectedNode == null)
            {
                treeView.SelectedNode = treeView.Nodes[0];
            }

            if (IsTreeNodeFile(treeView.SelectedNode) == true)
            {
                treeView.SelectedNode = treeView.SelectedNode.Parent;
            }

            treeView.LabelEdit = rename;

            TreeNode createdNode = treeView.SelectedNode.Nodes.Add(name);
            IProjectFolder createdFolder = new ProjectFolder() { Name = name };

            if (IsTreeNodeFolder(treeView.SelectedNode) == true)
            {
                IProjectFolder folder = GetTreeNodeFolder(treeView.SelectedNode);

                folder.Folders.Add(createdFolder);
                createdFolder.Parent = folder;
            }
            else
            {
                Global.Instance.Project.Folders.Add(createdFolder);
            }

            createdNode.ImageIndex = Global.FolderClosedImageIndex;
            createdNode.SelectedImageIndex = createdNode.ImageIndex;
            createdNode.Tag = createdFolder;

            treeView.SelectedNode.Expand();
            if (rename == true) createdNode.BeginEdit();

            treeView.SelectedNode = createdNode;
            Global.Instance.SaveProject(false, false);
            ((IProperties)((object[])(Global.Instance.GetService(typeof(IProperties))))[0]).SelectedFile = IsTreeNodeFile(treeView.SelectedNode) == true ? GetTreeNodeFile(treeView.SelectedNode) : null;
            return createdNode;
        }

        public TreeNode AddFolder()
        {
            return AddFolder("New Folder", true);
        }

        public TreeNode AddFile(TreeNode node, IProjectFile file, ITemplate template, bool openFile)
        {
            if (file != null)
            {
                IProjectFolder folder = GetTreeNodeFolder(node);
                file.Parent = IsTreeNodeFolder(node) == true ? folder : null;

                if (file.Parent == null)
                {
                    file.Path = String.Format("{0}\\{1}\\{2}", Path.GetDirectoryName(Global.Instance.Project.Path), Global.SourceCodeFolderName, file.Name);
                    string extension = Path.GetExtension(file.Path);
                    if (extension == "") file.Path += "." + (template != null ? template.FileExtension : Path.GetExtension(file.Path));
                    Global.Instance.Project.Files.Add(file);
                }
                else
                {
                    string folderPath = GetFileFolderPath(file);
                    file.Path = String.Format("{0}\\{1}\\{2}\\{3}", Path.GetDirectoryName(Global.Instance.Project.Path), Global.SourceCodeFolderName, folderPath, file.Name);
                    string extension = Path.GetExtension(file.Path);
                    if (extension == "") file.Path += "." + (template != null ? template.FileExtension : Path.GetExtension(file.Path));
                    folder.Files.Add(file);
                }

                file.Name = Path.GetFileName(file.Path);
                file.UpdateBuildAction();
                TreeNode fileNode = CreateFile(Global.Instance.Project, file, template);
                if (openFile == true) Global.Instance.OpenFile(file.Path, file);
                node.Nodes.Add(fileNode);
                node.Expand();
                treeView.Sort();
                if (template != null)
                {
                    string parsedCode = TemplateParser.Parse(template);
                    using (StreamWriter writer = new StreamWriter(file.Path))
                    {
                        writer.Write(parsedCode);
                    }
                }

                if (Global.Instance.Project.Type == ProjectType.Jass && Path.GetExtension(file.Path).Substring(1) == Global.JassFileExtension) JassInheritanceManager.AddFile(file);
                UpdateAllTreeNodeImageIndexes();
                Global.Instance.SaveProject(false, false);
                ((IProperties)((object[])(Global.Instance.GetService(typeof(IProperties))))[0]).SelectedFile = IsTreeNodeFile(treeView.SelectedNode) == true ? GetTreeNodeFile(treeView.SelectedNode) : null;
                return fileNode;
            }

            return null;
        }

        public TreeNode AddExistingFile(string path)
        {
            TreeNode node = treeView.SelectedNode ?? treeView.Nodes[0];
            while (IsTreeNodeFile(node))
            {
                node = node.Parent;
            }

            IProjectFile file = new ProjectFile() { Name = Path.GetFileName(path) };

            IProjectFolder folder = GetTreeNodeFolder(node);
            file.Parent = IsTreeNodeFolder(node) == true ? folder : null;

            if (file.Parent == null)
            {
                string newPath = String.Format("{0}\\{1}\\{2}", Path.GetDirectoryName(Global.Instance.Project.Path), Global.SourceCodeFolderName, file.Name);

                file.Path = newPath;
                DirectoryHelper.CreatePath(newPath);
                if (!File.Exists(newPath))
                {
                    File.Copy(path, newPath);
                }

                Global.Instance.Project.Files.Add(file);
            }
            else
            {
                string folderPath = GetFileFolderPath(file);
                string newPath = String.Format("{0}\\{1}\\{2}\\{3}", Path.GetDirectoryName(Global.Instance.Project.Path),
                    Global.SourceCodeFolderName, folderPath, file.Name);

                file.Path = newPath;
                if (!File.Exists(newPath))
                {
                    File.Copy(path, newPath);
                }
                folder.Files.Add(file);
            }

            file.UpdateBuildAction();
            TreeNode fileNode = CreateFile(Global.Instance.Project, file);
            node.Nodes.Add(fileNode);
            node.Expand();
            treeView.Sort();
            if (Global.Instance.Project.Type == ProjectType.Jass && Path.GetExtension(file.Path).Substring(1) == Global.JassFileExtension) JassInheritanceManager.AddFile(file);
            UpdateAllTreeNodeImageIndexes();
            Global.Instance.SaveProject(false, false);
            ((IProperties)((object[])(Global.Instance.GetService(typeof(IProperties))))[0]).SelectedFile = IsTreeNodeFile(treeView.SelectedNode) == true ? GetTreeNodeFile(treeView.SelectedNode) : null;
            return fileNode;
        }

        public TreeNode CreateNewFile(bool defaultFile)
        {
            TreeNode node = treeView.SelectedNode ?? treeView.Nodes[0];
            if (IsTreeNodeFile(node) == true)
            {
                node = node.Parent;
            }

            if (IsTreeNodeFile(node) == false)
            {
                IProjectFile file = Global.Instance.CreateFile(defaultFile);
                return AddFile(node, file, ((ICreateFileWizard)((object[])(Global.Instance.GetService(typeof(ICreateFileWizard))))[0]).Template, true);
            }

            return null;
        }

        public TreeNode CreateNewFile()
        {
            return CreateNewFile(false);
        }

        private void OnAddFolderClick(object sender, EventArgs e)
        {
            AddFolder();
        }

        private void OnTreeViewAfterLabelEdit(object sender, NodeLabelEditEventArgs e)
        {
            try
            {
                TreeNode node = treeView.SelectedNode;
                if (node == null)
                {
                    return;
                }

                string text = string.IsNullOrEmpty(e.Label) == true ? node.Text : e.Label;

                if (string.IsNullOrEmpty(text) == true)
                {
                    e.CancelEdit = true;

                    ShowError("Please enter a valid file name.");
                    node.BeginEdit();

                    return;
                }

                if (IsTreeNodeFolder(node) == true)
                {
                    e.CancelEdit = FolderExistsInContext(text, node) || IsInvalidFolderName(text);

                    if (e.CancelEdit == false)
                    {
                        IProjectFolder folder = GetTreeNodeFolder(node);
                        string folderPath = GetFullFolderPath(folder);

                        folder.Name = text;
                        node.Text = text;
                        treeView.LabelEdit = false;

                        string newFolderPath = String.Format("{0}\\{1}", (folder.Parent == null ? Path.GetDirectoryName(Global.Instance.Project.Path) +
                            "\\" + Global.SourceCodeFolderName : GetFullFolderPath(folder.Parent)), text);

                        try
                        {
                            if (Directory.Exists(folderPath) == false)
                            {
                                DirectoryHelper.CreateFolderPath(newFolderPath);
                            }
                            else
                            {
                                Directory.Move(folderPath, newFolderPath);
                            }

                            UpdateFolderFilesPaths(folder, newFolderPath);
                        }
                        catch (Exception exception)
                        {
                            ShowError(exception.Message);
                            treeView.LabelEdit = true;
                            node.BeginEdit();
                            return;
                        }
                    }
                    else
                    {
                        ShowError("Folder with the same name already exists/invalid folder name.");
                        node.BeginEdit();
                        return;
                    }
                }
                else if (IsTreeNodeFile(node) == true)
                {
                    e.CancelEdit = FileExistsInContext(text, node) || IsInvalidFileName(text);

                    if (e.CancelEdit == false)
                    {
                        IProjectFile file = GetTreeNodeFile(node);
                        if (Path.GetExtension(file.Name) == "." + Global.JassFileExtension && Global.Instance.Project.Type == ProjectType.Jass) JassInheritanceManager.AddFile(file);

                        if (FileNameHasExtension(file, text) == false)
                        {
                            e.CancelEdit = true;
                            ShowError("Please give the file a valid extension.");
                            node.BeginEdit();
                            return;
                        }

                        string newPath = Path.GetDirectoryName(file.Path) + "\\" + text;
                        File.Move(file.Path, newPath);
                        file.Path = newPath;
                        file.Name = text;
                        node.Text = text;
                        treeView.LabelEdit = false;
                        treeView.SelectedNode = node;
                    }
                    else
                    {
                        ShowError("File with the same name already exists/invalid file name.");
                        node.BeginEdit();
                        return;
                    }
                }
                else
                {
                    e.CancelEdit = IsInvalidFileName(text) == true;

                    if (e.CancelEdit == false)
                    {
                        string path = Global.Instance.Project.Path;
                        string newPath = String.Format("{0}\\{1}.{2}", Path.GetDirectoryName(path), text, Global.Instance.Project.GetProjectFileExtension());

                        File.Move(path, newPath);

                        Global.Instance.Project.Path = newPath;
                        Global.Instance.Project.Name = text;

                        node.Text = text;
                        treeView.LabelEdit = false;
                    }
                    else
                    {
                        ShowError("Invalid file name.");
                        node.BeginEdit();
                        return;
                    }
                }

                treeView.Sort();
                Global.Instance.SaveProject(false, false);
                UpdateAllTreeNodeImageIndexes();
                ((IProperties)((object[])(Global.Instance.GetService(typeof(IProperties))))[0]).SelectedFile = IsTreeNodeFile(treeView.SelectedNode) == true ? GetTreeNodeFile(treeView.SelectedNode) : null;
                updateFileNamesTimer.Start();
            }
            catch (IOException exception)
            {
                MessageBox.Show(exception.Message, "Moonlite Map Studio");
            }
        }

        private void OnTreeViewAfterExpand(object sender, TreeViewEventArgs e)
        {
            if (IsTreeNodeFolder(e.Node) != true)
            {
                return;
            }

            if (e.Node.IsExpanded == true)
            {
                e.Node.ImageIndex = Global.FolderOpenImageIndex;
                e.Node.SelectedImageIndex = e.Node.ImageIndex;
            }
            else
            {
                e.Node.ImageIndex = Global.FolderClosedImageIndex;
                e.Node.SelectedImageIndex = e.Node.ImageIndex;
            }
        }

        private void OnTreeViewAfterCollapse(object sender, TreeViewEventArgs e)
        {
            if (IsTreeNodeFolder(e.Node) != true)
            {
                return;
            }

            if (e.Node.IsExpanded == true)
            {
                e.Node.ImageIndex = Global.FolderOpenImageIndex;
                e.Node.SelectedImageIndex = e.Node.ImageIndex;
            }
            else
            {
                e.Node.ImageIndex = Global.FolderClosedImageIndex;
                e.Node.SelectedImageIndex = e.Node.ImageIndex;
            }
        }

        private void OnTreeViewMouseDown(object sender, MouseEventArgs e)
        {
            treeView.SelectedNode = treeView.GetNodeAt(e.X, e.Y);
            ((IProperties)((object[])(Global.Instance.GetService(typeof(IProperties))))[0]).SelectedFile = IsTreeNodeFile(treeView.SelectedNode) == true ? GetTreeNodeFile(treeView.SelectedNode) : null;
        }

        private void OnAddNewItemClick(object sender, EventArgs e)
        {
            CreateNewFile();
        }

        private void OnAddExistingItemClick(object sender, EventArgs e)
        {
            string extension = String.Format(".{0}", (Global.Instance.Project.Type == ProjectType.Galaxy ? Global.GalaxyFileExtension : Global.JassFileExtension));
            using (OpenFileDialog addExistingItemDialog = new OpenFileDialog { Filter = "All Files (*.*)|*.*" })
            {
                if (addExistingItemDialog.ShowDialog() == DialogResult.OK)
                {
                    AddExistingFile(addExistingItemDialog.FileName);
                }
            }
        }

        private void UpdateTreeNodes(TreeNode node)
        {
            foreach (TreeNode childNode in node.Nodes)
            {
                if (IsTreeNodeFile(childNode))
                {
                    IProjectFile nodeFile = GetTreeNodeFile(childNode);
                    childNode.Text = nodeFile.Name;
                    childNode.Tag = nodeFile;
                    childNode.ImageIndex = nodeFile.GetImageIndex();
                    childNode.SelectedImageIndex = childNode.ImageIndex;
                }
                else if (IsTreeNodeFolder(childNode))
                {
                    UpdateTreeNodes(childNode);
                }
            }
        }

        private void UpdateAllTreeNodeImageIndexes()
        {
            UpdateTreeNodes(treeView.Nodes[0]);
        }

        private static string GenerateTreeNodePath(TreeNode node)
        {
            string nodePath = "";
            TreeNode currentNode = node;
            while (currentNode != null)
            {
                nodePath = String.Format("{0}\\{1}", currentNode.Text, nodePath);
                currentNode = currentNode.Parent;
            }

            if (nodePath.EndsWith("\\") == true)
            {
                nodePath = nodePath.Substring(0, nodePath.Length - 1);
            }

            return nodePath;
        }

        private static void UpdateParentOfChildFiles(IProjectFolder folder)
        {
            foreach (IProjectFile childFile in folder.Files)
            {
                childFile.Parent = folder;
            }
        }

        private static void UpdateParentOfChildFolders(IProjectFolder folder)
        {
            foreach (IProjectFolder childFolder in folder.Folders)
            {
                childFolder.Parent = folder;

                if (childFolder.Folders.Count > 0)
                {
                    UpdateParentOfChildFolders(childFolder);
                }

                if (childFolder.Files.Count > 0)
                {
                    UpdateParentOfChildFiles(folder);
                }
            }
        }

        private void CutNode(TreeNode node)
        {
            CopyNode(node);
            Paste += OnIProjectManagerPaste;
        }

        private void CopyNode(TreeNode node)
        {
            Clipboard.SetDataObject(node);
            SourceNode = node;

            TagType = IsTreeNodeFolder(node) == true ? "Folder" : IsTreeNodeFile(node) == true ? "File" : "";
            FolderTag = GetTreeNodeFolder(node);
            FileTag = GetTreeNodeFile(node);
        }

        private void PasteNode(TreeNode node)
        {
            if (FolderTag == null && FileTag == null)
            {
                itmPaste.Enabled = false;
                return;
            }

            node = node ?? treeView.Nodes[0];

            if (FolderTag != null)
            {
                UpdateFolderFilesPaths(FolderTag, GetFullFolderPath(FolderTag));
            }

            TreeNode sourceNode = Clipboard.GetDataObject().GetData(typeof(TreeNode)) as TreeNode;

            if (TagType == "Folder")
            {
                IProjectFolder folder = new ProjectFolder();
                TreeNode parentNode = sourceNode.Text == node.Text ? node.Parent : node;

                if (IsTreeNodeFolder(parentNode) == true)
                {
                    folder.Parent = GetTreeNodeFolder(parentNode);
                }

                CreateCopyOfFolder(FolderTag, folder);

                UpdateFolderFilesPaths(folder, GetFullFolderPath(folder));
                sourceNode.Tag = folder;
            }
            else if (TagType == "File")
            {
                sourceNode.Tag = new ProjectFile() { Name = String.Format("Copy of {0}", FileTag.Name) };
            }

            IProjectFolder sourceFolder = GetTreeNodeFolder(sourceNode);
            IProjectFile sourceFile = GetTreeNodeFile(sourceNode);

            if (sourceFolder != null)
            {
                UpdateParentOfChildFiles(sourceFolder);
                UpdateParentOfChildFolders(sourceFolder);
            }

            if (sourceNode.Text == node.Text)
            {
                node = node.Parent;
            }

            sourceNode.Text = String.Format("Copy of {0}", sourceNode.Text);
            if (IsTreeNodeFolder(node) == true)
            {
                IProjectFolder folder = GetTreeNodeFolder(node);

                if (sourceFolder != null)
                {
                    sourceFolder.Parent = folder;
                    folder.Folders.Add(sourceFolder);

                    CreateFolder(FolderTag, sourceFolder);
                }
                else if (sourceFile != null)
                {
                    sourceFile.Parent = folder;
                    sourceFile.Path = String.Format("{0}\\{1}.{2}", GetFullFolderPath(sourceFile.Parent), sourceFile.Name,
                        Path.GetExtension(sourceFile.Path).Substring(1));

                    folder.Files.Add(sourceFile);

                    FileStream sourceFileStream = File.Create(FileTag.Path);
                    FileStream targetFileStream = File.Create(sourceFile.Path);

                    DirectoryHelper.CreatePath(sourceFile.Path);
                    DirectoryHelper.MakeFolderWritable(sourceFile.Path);
                    using (StreamWriter writer = new StreamWriter(targetFileStream))
                    {
                        using (StreamReader reader = new StreamReader(sourceFileStream))
                        {
                            writer.Write(reader.ReadToEnd());
                        }
                    }

                    sourceFileStream.Close();
                    targetFileStream.Close();
                }

                node.Nodes.Add(sourceNode);
                node.Expand();
            }
            else if (IsTreeNodeFile(node) == true)
            {
                IProjectFolder folder = GetTreeNodeFolder(node.Parent);

                if (sourceFolder != null)
                {
                    sourceFolder.Parent = folder;
                    folder.Folders.Add(sourceFolder);

                    CreateFolder(FolderTag, sourceFolder);
                }
                else if (sourceFile != null)
                {
                    sourceFile.Parent = folder;
                    sourceFile.Path = String.Format("{0}\\{1}.{2}", GetFullFolderPath(sourceFile.Parent), sourceFile.Name,
                        Path.GetExtension(sourceFile.Path).Substring(1));

                    folder.Files.Add(sourceFile);

                    FileStream sourceFileStream = File.Create(FileTag.Path);
                    FileStream targetFileStream = File.Create(sourceFile.Path);

                    DirectoryHelper.CreatePath(sourceFile.Path);
                    DirectoryHelper.MakeFolderWritable(sourceFile.Path);
                    using (StreamWriter writer = new StreamWriter(targetFileStream))
                    {
                        using (StreamReader reader = new StreamReader(sourceFileStream))
                        {
                            writer.Write(reader.ReadToEnd());
                        }
                    }

                    sourceFileStream.Close();
                    targetFileStream.Close();
                }

                node.Parent.Nodes.Add(sourceNode);
                node.Expand();
            }
            else
            {
                if (sourceFolder != null)
                {
                    Global.Instance.Project.Folders.Add(sourceFolder);

                    CreateFolder(FolderTag, sourceFolder);
                }
                else if (sourceFile != null)
                {
                    sourceFile.Path = String.Format("{0}\\{1}\\{2}.{3}", Path.GetDirectoryName(Global.Instance.Project.Path),
                        Global.SourceCodeFolderName, sourceFile.Name, Path.GetExtension(sourceFile.Path).Substring(1));

                    Global.Instance.Project.Files.Add(sourceFile);

                    FileStream sourceFileStream = File.Create(FileTag.Path);
                    FileStream targetFileStream = File.Create(sourceFile.Path);

                    DirectoryHelper.CreatePath(sourceFile.Path);
                    DirectoryHelper.MakeFolderWritable(sourceFile.Path);
                    using (StreamWriter writer = new StreamWriter(targetFileStream))
                    {
                        using (StreamReader reader = new StreamReader(sourceFileStream))
                        {
                            writer.Write(reader.ReadToEnd());
                        }
                    }

                    sourceFileStream.Close();
                    targetFileStream.Close();
                }

                treeView.Nodes[0].Nodes.Add(sourceNode);
                treeView.Nodes[0].Expand();
            }

            treeView.SelectedNode = sourceNode;
            PastedNode = sourceNode;

            Global.Instance.SaveProject(false, false);
            UpdateAllTreeNodeImageIndexes();

            OnPaste(EventArgs.Empty);
        }

        private void MoveNode(TreeNode source, TreeNode destination)
        {
            List<TreeNode> list = new List<TreeNode>();
            list.Add(source);
            MoveNode(list, destination);
        }

        private void MoveNode(List<TreeNode> sources, TreeNode destination)
        {
            foreach (TreeNode source in sources)
            {
                TreeNodeCollection otherNodes = GetTreeNodesOnSameLevel(source);
                if ((otherNodes.Contains(destination) && !IsTreeNodeFolder(destination)) || (!IsTreeNodeFile(destination) && source.Parent == destination))
                {
                    ShowError("Cannot move '" + source.Text + "'. The destination folder is the source as the source folder.");
                    return;
                }
                if (IsTreeNodeFile(source))
                {
                    IProjectFile file = GetTreeNodeFile(source);
                    if (IsTreeNodeFile(destination))
                    {
                        IProjectFile destinationFile = GetTreeNodeFile(destination);
                        if (file.Parent == null)
                        {
                            Global.Instance.Project.Files.Remove(file);
                        }
                        else
                        {
                            file.Parent.Files.Remove(file);
                        }
                        file.Parent = destinationFile.Parent;
                        if (file.Parent == null)
                        {
                            Global.Instance.Project.Files.Add(file);
                        }
                        else
                        {
                            file.Parent.Files.Add(file);
                        }
                        string oldPath = file.Path;
                        file.Path = GetFileFullPath(file);
                        File.Move(oldPath, file.Path);
                        source.Parent.Nodes.Remove(source);
                        destination.Parent.Nodes.Add(source);
                    }
                    else if (IsTreeNodeFolder(destination))
                    {
                        IProjectFolder destinationFolder = GetTreeNodeFolder(destination);
                        if (file.Parent == null)
                        {
                            Global.Instance.Project.Files.Remove(file);
                        }
                        else
                        {
                            file.Parent.Files.Remove(file);
                        }
                        file.Parent = destinationFolder;
                        if (file.Parent == null)
                        {
                            Global.Instance.Project.Files.Add(file);
                        }
                        else
                        {
                            file.Parent.Files.Add(file);
                        }
                        string oldPath = file.Path;
                        file.Path = GetFileFullPath(file);
                        File.Move(oldPath, file.Path);
                        source.Parent.Nodes.Remove(source);
                        destination.Nodes.Add(source);
                    }
                    else
                    {
                        if (file.Parent == null)
                        {
                            Global.Instance.Project.Files.Remove(file);
                        }
                        else
                        {
                            file.Parent.Files.Remove(file);
                        }
                        file.Parent = null;
                        Global.Instance.Project.Files.Add(file);
                        string oldPath = file.Path;
                        file.Path = GetFileFullPath(file);
                        File.Move(oldPath, file.Path);
                        source.Parent.Nodes.Remove(source);
                        destination.Nodes.Add(source);
                    }
                }
                else if (IsTreeNodeFolder(source))
                {
                    IProjectFolder folder = GetTreeNodeFolder(source);
                    if (IsTreeNodeFile(destination))
                    {
                        IProjectFile destinationFile = GetTreeNodeFile(destination);
                        if (folder.Parent == null)
                        {
                            Global.Instance.Project.Folders.Remove(folder);
                        }
                        else
                        {
                            folder.Parent.Folders.Remove(folder);
                        }
                        folder.Parent = destinationFile.Parent;
                        if (folder.Parent == null)
                        {
                            Global.Instance.Project.Folders.Add(folder);
                        }
                        else
                        {
                            folder.Parent.Folders.Add(folder);
                        }
                        UpdateFolderFilesPaths(folder, GetFullFolderPath(folder));
                        Directory.Move(GetFullFolderPath(folder), folder.Parent == null ? Global.Instance.Project.Path : GetFullFolderPath(folder.Parent));
                        source.Parent.Nodes.Remove(source);
                        destination.Parent.Nodes.Add(source);
                    }
                    else if (IsTreeNodeFolder(destination))
                    {
                        IProjectFolder destinationFolder = GetTreeNodeFolder(destination);
                        if (folder.Parent == null)
                        {
                            Global.Instance.Project.Folders.Remove(folder);
                        }
                        else
                        {
                            folder.Parent.Folders.Remove(folder);
                        }

                        string fullFolderPath = GetFullFolderPath(folder);
                        string fullDestinationFolderPath = GetFullFolderPath(destinationFolder) + "\\" + Path.GetFileName(fullFolderPath);
                        Directory.Move(fullFolderPath, fullDestinationFolderPath);
                        folder.Parent = destinationFolder;
                        if (folder.Parent == null)
                        {
                            Global.Instance.Project.Folders.Add(folder);
                        }
                        else
                        {
                            folder.Parent.Folders.Add(folder);
                        }

                        UpdateFolderFilesPaths(folder, GetFullFolderPath(folder));
                        source.Parent.Nodes.Remove(source);
                        destination.Nodes.Add(source);
                    }
                    else
                    {
                        if (folder.Parent == null)
                        {
                            Global.Instance.Project.Folders.Remove(folder);
                        }
                        else
                        {
                            folder.Parent.Folders.Remove(folder);
                        }

                        string fullFolderPath = GetFullFolderPath(folder);
                        Directory.Move(fullFolderPath, Path.GetDirectoryName(Global.Instance.Project.Path) + "\\" + Global.SourceCodeFolderName + "\\" + Path.GetFileName(fullFolderPath));
                        folder.Parent = null;
                        Global.Instance.Project.Folders.Add(folder);
                        UpdateFolderFilesPaths(folder, GetFullFolderPath(folder));
                        source.Parent.Nodes.Remove(source);
                        destination.Nodes.Add(source);
                    }
                }
            }

            Global.Instance.SaveProject(false, true);
        }

        private static void DeleteNode(TreeNode node, bool prompt)
        {
            try
            {
                if (IsTreeNodeFolder(node) == true)
                {
                    DialogResult result = prompt == true ? MessageBox.Show(String.Format("'{0}' and all of its contents will be deleted permanently.", node.Text),
                        "Moonlite Map Studio", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1) : DialogResult.OK;

                    if (result == DialogResult.OK)
                    {
                        IProjectFolder folder = GetTreeNodeFolder(node);
                        if (folder.Parent == null)
                        {
                            Global.Instance.Project.Folders.Remove(folder);
                        }
                        else
                        {
                            folder.Parent.Folders.Remove(folder);
                        }

                        node.Remove();

                        string folderPath = GetFullFolderPath(folder);
                        DirectoryHelper.MakeFolderWritable(folderPath);

                        if (Directory.Exists(folderPath) == true)
                        {
                            DirectoryInfo directoryInfo = new DirectoryInfo(folderPath);
                            Array.ForEach(directoryInfo.GetFiles(), file => file.Delete());
                        }

                        DirectoryHelper.DeleteDirectory(folderPath);
                    }

                    Global.Instance.SaveProject(false, false);
                }
                else if (IsTreeNodeFile(node) == true)
                {
                    DialogResult result = prompt == true ? MessageBox.Show(String.Format("'{0}' will be deleted permanently.", node.Text),
                        "Moonlite Map Studio", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1) : DialogResult.OK;

                    if (result == DialogResult.OK)
                    {
                        IProjectFile file = GetTreeNodeFile(node);
                        if (file.Parent == null)
                        {
                            Global.Instance.Project.Files.Remove(file);
                        }
                        else
                        {
                            file.Parent.Files.Remove(file);
                        }

                        node.Remove();

                        DirectoryHelper.MakeFolderWritable(file.Path);
                        File.Delete(file.Path);
                    }

                    Global.Instance.SaveProject(false, false);
                }
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
        }

        private void RenameNode(TreeNode node)
        {
            treeView.LabelEdit = true;
            node.BeginEdit();
        }

        private void OnIProjectManagerPaste(object sender, EventArgs e)
        {
            Paste -= OnIProjectManagerPaste;
            DeleteNode(SourceNode, false);
            UpdateAllTreeNodeImageIndexes();

            Global.Instance.SaveProject(false, false);
        }

        private static void CreateFolder(IProjectFolder sourceFolder, IProjectFolder folder)
        {
            string folderPath = GetFullFolderPath(folder);
            DirectoryHelper.CreateFolderPath(folderPath);
            DirectoryHelper.MakeFolderWritable(folderPath);
            Directory.CreateDirectory(folderPath);

            foreach (IProjectFile file in folder.Files)
            {
                FileStream fileStream = File.Create(file.Path);
                FileStream sourceFileStream = File.Create(FindFileInSourceFolder(sourceFolder, file).Path);
                using (StreamWriter writer = new StreamWriter(fileStream))
                {
                    using (StreamReader reader = new StreamReader(sourceFileStream))
                    {
                        writer.Write(reader.ReadToEnd());
                    }
                }

                fileStream.Close();
                sourceFileStream.Close();
            }

            foreach (IProjectFolder childFolder in folder.Folders)
            {
                CreateFolder(sourceFolder, childFolder);
            }
        }

        private static void CreateCopyOfFolderChild(IProjectFolder source, IProjectFolder currentFolder)
        {
            DirectoryHelper.CreateFolderPath(GetFullFolderPath(currentFolder));
            foreach (IProjectFile file in source.Files)
            {
                IProjectFile newFile = new ProjectFile
                {
                    Name = file.Name,
                    Parent = currentFolder,
                };

                newFile.Path = String.Format("{0}\\{1}.{2}", GetFullFolderPath(currentFolder), newFile.Name, Path.GetExtension(newFile.Path).Substring(1));
                FileStream sourceFileStream = File.Create(file.Path);
                FileStream targetFileStream = File.Create(newFile.Path);
                DirectoryHelper.CreatePath(newFile.Path);
                DirectoryHelper.MakeFolderWritable(newFile.Path);
                using (StreamWriter writer = new StreamWriter(targetFileStream))
                {
                    using (StreamReader reader = new StreamReader(sourceFileStream))
                    {
                        writer.Write(reader.ReadToEnd());
                    }

                }

                sourceFileStream.Close();
                targetFileStream.Close();
                currentFolder.Files.Add(newFile);
            }

            foreach (IProjectFolder folder in source.Folders)
            {
                IProjectFolder newFolder = new ProjectFolder()
                {
                    Name = folder.Name,
                    Parent = currentFolder
                };

                CreateCopyOfFolderChild(FindFolderInSourceFolder(source, folder), newFolder);
                currentFolder.Folders.Add(newFolder);
            }
        }

        private static void CreateCopyOfFolder(IProjectFolder source, IProjectFolder folder)
        {
            folder.Name = String.Format("Copy of {0}", source.Name);
            CreateCopyOfFolderChild(source, folder);
        }

        public static IProjectFile FindFileInSourceFolder(IProjectFolder sourceFolder, IProjectFile file)
        {
            foreach (IProjectFile childFile in sourceFolder.Files)
            {
                int childFileLevel = GetFileFolderLevel(childFile);
                int findFileLevel = GetFileFolderLevel(file);
                if (childFile.Name == file.Name && childFileLevel == findFileLevel)
                {
                    FoundSourceFile = childFile;
                }
            }

            while (FoundSourceFile == null)
            {
                foreach (IProjectFolder childFolder in sourceFolder.Folders)
                {
                    FindFileInSourceFolder(childFolder, file);
                    if (FoundSourceFile != null)
                    {
                        break;
                    }
                }
            }

            return FoundSourceFile;
        }

        public static IProjectFolder FindFolderInSourceFolder(IProjectFolder sourceFolder, IProjectFolder folder)
        {
            foreach (IProjectFolder childFolder in sourceFolder.Folders)
            {
                int childFolderLevel = GetFolderFolderLevel(childFolder);
                int findFolderLevel = GetFolderFolderLevel(folder);
                if (childFolder.Name == folder.Name && childFolderLevel == findFolderLevel)
                {
                    FoundSourceFolder = childFolder;
                }
            }

            while (FoundSourceFolder == null)
            {
                foreach (IProjectFolder childFolder in sourceFolder.Folders)
                {
                    FindFolderInSourceFolder(childFolder, folder);
                    if (FoundSourceFolder != null)
                    {
                        break;
                    }
                }
            }

            return FoundSourceFolder;
        }

        public static int GetFileFolderLevel(IProjectFile file)
        {
            int level = 0;

            IProjectFolder currentParent = file.Parent;
            while (currentParent != null)
            {
                level++;
                currentParent = currentParent.Parent;
            }

            return level;
        }

        private static int GetFolderFolderLevel(IProjectFolder folder)
        {
            int level = 0;

            IProjectFolder currentParent = folder.Parent;
            while (currentParent != null)
            {
                level++;
                currentParent = currentParent.Parent;
            }

            return level;
        }

        private void OnTreeViewKeyUp(object sender, KeyEventArgs e)
        {
            ((IProperties)((object[])(Global.Instance.GetService(typeof(IProperties))))[0]).SelectedFile = IsTreeNodeFile(treeView.SelectedNode) == true ? GetTreeNodeFile(treeView.SelectedNode) : null;
        }

        private void OnRenameClick(object sender, EventArgs e)
        {
            RenameNode(treeView.SelectedNode);
        }

        private void OnDeleteClick(object sender, EventArgs e)
        {
            DeleteNode(treeView.SelectedNode, true);
        }

        private void OnCutClick(object sender, EventArgs e)
        {
            CutNode(treeView.SelectedNode);
        }

        private void OnCopyClick(object sender, EventArgs e)
        {
            CopyNode(treeView.SelectedNode);
        }

        private void OnPasteClick(object sender, EventArgs e)
        {
            PasteNode(treeView.SelectedNode);
        }

        private void OnOpenFolderInWindowsExplorerClick(object sender, EventArgs e)
        {
            Process.Start(IsTreeNodeFolder(treeView.SelectedNode) == true ? GetFullFolderPath(GetTreeNodeFolder(treeView.SelectedNode)) :
                IsTreeNodeFile(treeView.SelectedNode) == true ? Path.GetDirectoryName(GetTreeNodeFile(treeView.SelectedNode).Path) :
                Global.Instance.Project.Path);
        }

        private static void UpdateFolderFilesPaths(IProjectFolder folder, string folderPath)
        {
            foreach (IProjectFile file in folder.Files)
            {
                file.Path = String.Format("{0}\\{1}", folderPath, file.Name);
            }

            foreach (IProjectFolder childFolder in folder.Folders)
            {
                UpdateFolderFilesPaths(childFolder, String.Format("{0}\\{1}", folderPath, childFolder.Name));
            }
        }

        private static bool FileNameHasExtension(IProjectFile file, string inputName)
        {
            return Path.GetExtension(inputName) != "";
        }

        private static string GetFileNameWithoutExtension(IProjectFile file, string inputName)
        {
            return inputName.Replace(String.Format(".{0}", Path.GetExtension(inputName)), "");
        }

        private static string GetFileNameWithExtension(IProjectFile file, string inputName)
        {
            return inputName += String.Format(".{0}", Path.GetExtension(inputName));
        }

        private static string GetFileFullPath(IProjectFile file)
        {
            return String.Format("{0}\\{1}", (file.Parent == null ? Path.GetDirectoryName(Global.Instance.Project.Path) + "\\" + Global.SourceCodeFolderName :
                GetFullFolderPath(file.Parent)), file.Name);
        }

        private static string GetFileFullPathWithNewName(IProjectFile file, string name)
        {
            name = name.Replace(String.Format(".{0}", Path.GetExtension(file.Path)), "");
            return String.Format("{0}\\{1}.{2}", (file.Parent == null ? Path.GetDirectoryName(Global.Instance.Project.Path) + "\\" + Global.SourceCodeFolderName :
                GetFullFolderPath(file.Parent)), GetFileNameWithoutExtension(file, name), Path.GetExtension(file.Path));
        }

        public static string GetFileFolderPath(IProjectFile file)
        {
            return GetFolderPath(file.Parent);
        }

        public static string GetFolderPath(IProjectFolder folder)
        {
            string folderPath = "";
            IProjectFolder currentFolder = folder;
            while (currentFolder != null)
            {
                folderPath = String.Format("{0}\\{1}", currentFolder.Name, folderPath);
                currentFolder = currentFolder.Parent;
            }

            if (folderPath.EndsWith("\\") == true)
            {
                folderPath = folderPath.Substring(0, folderPath.Length - 1);
            }

            return folderPath;
        }

        public static string GetFullFolderPath(IProjectFolder folder)
        {
            return String.Format("{0}\\{1}\\{2}", Path.GetDirectoryName(Global.Instance.Project.Path), Global.SourceCodeFolderName, GetFolderPath(folder));
        }

        public static bool IsTreeNodeFolder(TreeNode node)
        {
            if (node == null) return false;
            return node.Tag is IProjectFolder;
        }

        public static bool IsTreeNodeFile(TreeNode node)
        {
            if (node == null) return false;
            return node.Tag is IProjectFile;
        }

        public static IProjectFolder GetTreeNodeFolder(TreeNode node)
        {
            return node.Tag as IProjectFolder;
        }

        public static IProjectFile GetTreeNodeFile(TreeNode node)
        {
            return node.Tag as IProjectFile;
        }

        public static TreeNodeCollection GetTreeNodesOnSameLevel(TreeNode treeNode)
        {
            return treeNode.Parent.Nodes;
        }

        public static bool IsInvalidFileName(string name)
        {
            return name.IndexOfAny(Path.GetInvalidFileNameChars()) > -1;
        }

        public static bool IsInvalidFolderName(string name)
        {
            return name.IndexOfAny(Path.GetInvalidPathChars()) > -1;
        }

        public static TreeNode CreateFile(IProject project, IProjectFile file)
        {
            DirectoryHelper.CreatePath(file.Path);
            int imageIndex = file.GetImageIndex();
            return new TreeNode(file.Name) { ImageIndex = imageIndex, SelectedImageIndex = imageIndex, Tag = file };
        }

        public static TreeNode CreateFile(IProject project, IProjectFile file, ITemplate template)
        {
            DirectoryHelper.CreatePath(file.Path);

            if (template != null)
            {
                string code = File.Exists(template.FileName) == true ? TemplateParser.Parse(template) : "";
                using (StreamWriter writer = File.CreateText(file.Path))
                {
                    writer.Write(code);
                }
            }
            else
            {
                File.Create(file.Path).Close();
            }

            int imageIndex = file.GetImageIndex();
            return new TreeNode(file.Name) { ImageIndex = imageIndex, SelectedImageIndex = imageIndex, Tag = file };
        }

        private static bool FolderExistsInContext(string folderName, TreeNode folderNode)
        {
            foreach (TreeNode node in GetTreeNodesOnSameLevel(folderNode))
            {
                if (node.Text != folderName || node == folderNode)
                {
                    continue;
                }

                return true;
            }

            return false;
        }

        private static bool FileExistsInContext(string fileName, TreeNode fileNode)
        {
            foreach (TreeNode node in GetTreeNodesOnSameLevel(fileNode))
            {
                if (node.Text != fileName || node == fileNode)
                {
                    continue;
                }

                return true;
            }

            return false;
        }

        private static void ShowError(string message)
        {
            MessageBox.Show(String.Format("Unable to create object: {0}", message),
                "Error creating object", MessageBoxButtons.OK, MessageBoxIcon.Error,
                MessageBoxDefaultButton.Button1);
        }

        private void OnTreeViewDragDrop(object sender, DragEventArgs e)
        {
            System.Drawing.Point normalizedLocation = PointToClient(new System.Drawing.Point(e.X, e.Y));
            int x = normalizedLocation.X;
            int y = normalizedLocation.Y;
            if (e.Data.GetDataPresent(DataFormats.FileDrop) == true)
            {
                treeView.SelectedNode = treeView.GetNodeAt(x, y);
                treeView.SelectedNode = treeView.SelectedNode ?? treeView.Nodes[0];
                string[] filePaths = (string[])e.Data.GetData(DataFormats.FileDrop);
                Array.ForEach(filePaths, filePath =>
                {
                    if (File.Exists(filePath) == true)
                        AddExistingFile(filePath);
                });
            }
            else if (e.Data.GetDataPresent(typeof(TreeNode)))
            {
                TreeNode draggingTreeNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
                if (draggingTreeNode.Parent is AbstractCustomNode && !((AbstractCustomNode)draggingTreeNode.Parent).CanDragDropToChildren)
                {
                    e.Effect = DragDropEffects.None;
                    return;
                }

                if (draggingTreeNode.TreeView == treeView)
                {
                    treeView.SelectedNode = treeView.GetNodeAt(x, y);
                    treeView.SelectedNode = treeView.SelectedNode ?? treeView.Nodes[0];
                    MoveNode(draggingTreeNode, treeView.SelectedNode);
                }
            }
        }

        private void OnTreeViewDragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop) == true)
            {
                e.Effect = DragDropEffects.Copy;
            }
            else if (e.Data.GetDataPresent(typeof(TreeNode)))
            {
                TreeNode draggingTreeNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
                if (draggingTreeNode.Parent is AbstractCustomNode && !((AbstractCustomNode)draggingTreeNode.Parent).CanDragDropToChildren)
                {
                    e.Effect = DragDropEffects.None;
                    return;
                }

                if (draggingTreeNode.TreeView == treeView)
                {
                    e.Effect = DragDropEffects.Move;
                }
            }
        }

        private void OnTreeViewDragOver(object sender, DragEventArgs e)
        {
            treeView.SelectedNode = treeView.GetNodeAt(e.X, e.Y);
            if (e.Data.GetDataPresent(DataFormats.FileDrop) == true)
            {
                e.Effect = DragDropEffects.Copy;
            }
            else if (e.Data.GetDataPresent(typeof(TreeNode)))
            {
                TreeNode draggingTreeNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
                if (draggingTreeNode.Parent is AbstractCustomNode && !((AbstractCustomNode)draggingTreeNode.Parent).CanDragDropToChildren)
                {
                    e.Effect = DragDropEffects.None;
                    return;
                }

                if (draggingTreeNode.TreeView == treeView)
                {
                    e.Effect = DragDropEffects.Move;
                }
            }
        }

        private void OnTreeViewItemDrag(object sender, ItemDragEventArgs e)
        {
            DragDropEffects allowedEffects = DragDropEffects.None;
            object item = null;
            TreeNode node = e.Item as TreeNode;
            if (node.Parent is AbstractCustomNode && !((AbstractCustomNode)node.Parent).CanDragDropToChildren)
            {
                allowedEffects = DragDropEffects.None;
            }
            else if (IsTreeNodeFile(node) == true && !((IProjectFile)node.Tag).ReadOnly)
            {
                allowedEffects = DragDropEffects.Move;
            }
            else if (IsTreeNodeFolder(node) == true && !((IProjectFolder)node.Tag).ReadOnly)
            {
                allowedEffects = DragDropEffects.Move;
            }

            item = node;
            if (item != null)
            {
                DoDragDrop(item, allowedEffects);
            }
        }

        private void OpenNativeFileAction(IProjectFile file, TreeNode node)
        {
            Global.Instance.OpenFile(file.Path, file);
        }

        private void ExecuteOpenActionOnNode(TreeNode node)
        {
            bool shouldOpenNative = false;
            if (IsTreeNodeFile(node))
            {
                shouldOpenNative = true;
                IProjectFile file = GetTreeNodeFile(node);
                if (AssociatedActions.ContainsKey(Path.GetExtension(file.Path)))
                {
                    foreach (ProjectManagerOpenFileAction action in ((List<ProjectManagerOpenFileAction>)(AssociatedActions[Path.GetExtension(file.Path)])))
                    {
                        action.Invoke(file, node);
                    }
                }
            }

            ItemOpenEventArgs args = new ItemOpenEventArgs(node, false);
            if (ItemOpen != null)
            {
                ItemOpen(this, args);
            }

            if (shouldOpenNative)
            {
                IProjectFile file = GetTreeNodeFile(node);
                OpenNativeFileAction(file, node);
            }

            if (nativeNode != null)
            {
                nativeNode.ItemOpen(this, args);
            }
        }

        private void OnTreeViewNodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            ExecuteOpenActionOnNode(e.Node);
        }

        private void OnOpenClick(object sender, EventArgs e)
        {
            ExecuteOpenActionOnNode(treeView.SelectedNode);
        }

        private void OnViewProjectInformationClick(object sender, EventArgs e)
        {
            if (treeView.SelectedNode == treeView.Nodes[0])
            {
                Global.Instance.ViewProjectProperties();
            }
        }

        private void OnUpdateFileNamesTimerTick(object sender, EventArgs e)
        {
            if (Global.Instance == null || Global.Instance.Project == null) return;
            SuspendLayout();
            UpdateTreeNodes(treeView.Nodes[0]);
            ResumeLayout();
            updateFileNamesTimer.Stop();
        }

        private void AddRangeToImageList(ImageList imageList, ImageList.ImageCollection images)
        {
            foreach (Image image in images)
            {
                imageList.Images.Add(image);
            }
        }

        public ProjectManager()
        {
            InitializeComponent();
            try { Global.Instance.RegisterService(this, GetType().GetInterface("IProjectManager")); }
            catch { }
            treeView.TreeViewNodeSorter = new ProjectManagerNodeSorter();
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            AssociatedActions = new Hashtable();
            NativeAssociatedActions = new Hashtable();
            nativeFileAssociations = true;
            AddFileExtensionAssociation(".jx", OpenNativeFileAction);
            AddFileExtensionAssociation(".gx", OpenNativeFileAction);
            AddFileExtensionAssociation(".a", OpenNativeFileAction);
            AddFileExtensionAssociation(".xml", OpenNativeFileAction);
            AddFileExtensionAssociation(".txt", OpenNativeFileAction);
            nativeFileAssociations = false;
            updateFileNamesTimer = new Timer();
            updateFileNamesTimer.Interval = 100;
            updateFileNamesTimer.Tick += OnUpdateFileNamesTimerTick;
            CustomItems = new ToolStripItemCollection(contextMenuStrip, new ToolStripItem[] { });
        }
    }
}
